package com.jspx.txweb.ueditor.adaptor;

import com.jspx.boot.environment.Environment;
import com.jspx.boot.sign.LanguageRes;
import com.jspx.json.JSONObject;
import com.jspx.lucene.ChineseAnalyzer;
import com.jspx.sioc.annotation.Ref;
import com.jspx.txweb.IUserSession;
import com.jspx.txweb.action.UploadFileAction;
import com.jspx.txweb.annotation.HttpMethod;
import com.jspx.txweb.annotation.Safety;
import com.jspx.txweb.dao.UploadFileDAO;
import com.jspx.txweb.dispatcher.Dispatcher;
import com.jspx.txweb.support.ActionSupport;
import com.jspx.txweb.table.IUploadFile;
import com.jspx.txweb.util.RequestUtil;
import com.jspx.txweb.util.TXWebUtil;
import com.jspx.txweb.ueditor.ConfigManager;
import com.jspx.txweb.ueditor.define.*;
import com.jspx.txweb.ueditor.hunter.FileManager;
import com.jspx.txweb.ueditor.hunter.ImageHunter;
import com.jspx.txweb.ueditor.StorageManager;
import com.jspx.upload.MultipartRequest;
import com.jspx.upload.multipart.FileRenamePolicy;
import com.jspx.utils.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.Map;

/**
 * Created by chenyuan on 2015-8-6.
 * 为了兼容UEditor 的适配器而设计
 * UEditor 1.43版本
 *
 * 配置在
 *  ,serverUrl: "${action.getString('namespace')}ueditorcontroller.js.${suffix}"
 */
@HttpMethod(caption = "UEditor上传适配器")
public class UEditorAdaptor extends ActionSupport
{

    /**
     * DAO 对象
     */
    private UploadFileDAO uploadFileDAO;
    @Safety(request = false)
    @Ref(name = Environment.uploadFileDAO)
    public void setUploadFileDAO(UploadFileDAO uploadFileDAO) {
        this.uploadFileDAO = uploadFileDAO;
    }


    /**
     * 中文分词
     */
    private ChineseAnalyzer chineseAnalyzer;
    @Safety(request = false)
    @Ref(name = Environment.chineseAnalyzer)
    public void setChineseAnalyzer(ChineseAnalyzer chineseAnalyzer) {
        this.chineseAnalyzer = chineseAnalyzer;
    }

    /**
     * @return 得到配置允许上传的文件类型
     */
    public String getFileTypes() {
        String fileTypes = "*";
        IUserSession userSession = (IUserSession) getUserSession();
        if (userSession != null) {
            fileTypes = userSession.getRole(uploadFileDAO.getNamespace()).getUploadFileTypes();
        }
        if (StringUtil.isNULL(fileTypes) && config != null)
            fileTypes = config.getString(Environment.allowedTypes);
        return fileTypes;
    }

    /**
     * @return 得到安装路径
     * @throws Exception
     */
    private String getSetupPath() throws Exception {
        String setupPath = FileUtil.mendPath(config.getString(Environment.setupPath));
        if (!FileUtil.isDirectory(setupPath)) {
            setupPath = FileUtil.mendPath(FileUtil.getParentPath(getTemplatePath()));
            config.save(Environment.setupPath, setupPath);
            addFieldInfo(Environment.warningInfo, language.getLang(LanguageRes.setupConfigPathError) + ":" + setupPath);
        }
        return setupPath;
    }

    /**
     * @return 最大上传限制
     */
    public int getMaxPostSize() {
        IUserSession userSession = (IUserSession) getUserSession();
        int maxPostSize = 0;
        if (userSession != null) {
            maxPostSize = userSession.getRole(uploadFileDAO.getNamespace()).getUploadSize() * 1024;
        }
        if (maxPostSize < 0 && config != null)
            maxPostSize = config.getInt(Environment.uploadMaxSize) * 1024;
        return maxPostSize;
    }

    /**
     * @return 得到上传路径
     */
    public String getSaveDirectory() {
        return UploadFileAction.getUploadDirectory(config);
    }

    private ConfigManager configManager = null;

    /**
     * callback参数验证
     */
    public boolean validCallbackName ( String name ) {
        return name.matches("^[a-zA-Z_]+[\\w0-9_]*$");
    }

    public int getStartIndex () {

        return getInt("start", 0);
    }

    private String invoke() throws Exception {

        String saveDirectory = getSaveDirectory();
        String setupPath = getSetupPath();
        long maxSize = getMaxPostSize();
        String[] fileTypes = StringUtil.split(getFileTypes(), StringUtil.SEMICOLON);
        String actionType = getString("action",true);
        if ( actionType == null || !ActionMap.mapping.containsKey( actionType ) ) {
            return new BaseState( false, AppInfo.INVALID_ACTION ).toJSONString();
        }
        if ( this.configManager == null || !this.configManager.valid() ) {
            return new BaseState( false, AppInfo.CONFIG_ERROR ).toJSONString();
        }

        State state = null;
        int actionCode = ActionMap.getType( actionType );

        JSONObject jsonConfig = configManager.getAllConfig();
        if (jsonConfig.getInt("imageMaxSize")==0)
            jsonConfig.put("imageMaxSize",maxSize);

        if (jsonConfig.getInt("scrawlMaxSize")==0)
            jsonConfig.put("scrawlMaxSize",maxSize);

        jsonConfig.put("imagePathFormat", saveDirectory);
        jsonConfig.put("scrawlPathFormat", saveDirectory);
        jsonConfig.put("snapscreenPathFormat", saveDirectory);
        jsonConfig.put("catcherPathFormat", saveDirectory);
        jsonConfig.put("videoPathFormat", saveDirectory);
        jsonConfig.put("filePathFormat",saveDirectory);
        jsonConfig.put("imageManagerListPath", saveDirectory);
        jsonConfig.put("fileManagerListPath",saveDirectory);

        Map<String, Object> conf = null;

        switch ( actionCode ) {

            case ActionMap.CONFIG:
                return jsonConfig.toString();
            case ActionMap.UPLOAD_IMAGE:
            case ActionMap.UPLOAD_SCRAWL:
            case ActionMap.UPLOAD_VIDEO:
            case ActionMap.UPLOAD_FILE:
                conf = this.configManager.getConfig(actionCode );
                String filedName = (String) conf.get("fieldName");
                if ("true".equals(conf.get("isBase64")))
                {
                    state = base64Save(this.request.getParameter(filedName),saveDirectory,setupPath,maxSize);
                } else {
                    //saveDirectory,setupPath,maxSize,fileTypes
                    state = binarySave();
                }
                break;
            case ActionMap.CATCH_IMAGE:
                conf = configManager.getConfig(actionCode );
                String[] list = this.request.getParameterValues( (String)conf.get( "fieldName" ) );
                String[] filter = (String[])conf.get("filter");
                state = new ImageHunter(saveDirectory, System.currentTimeMillis()+"",maxSize,fileTypes,filter).capture( list );
                break;

            case ActionMap.LIST_IMAGE:
            case ActionMap.LIST_FILE:
                conf = configManager.getConfig( actionCode );
                int start = this.getStartIndex();
                int count = ObjectUtil.toInt(conf.get("count"));
                String[] allowFiles = (String[])conf.get("allowFiles");
                state = new FileManager(saveDirectory,Dispatcher.getRealPath(),allowFiles,count).listFile(start);
                break;
        }
        return state.toJSONString();

    }

    private State binarySave()
    {
        boolean isAjaxUpload = request.getHeader( "X_Requested_With" ) != null;
        if (isAjaxUpload)
        {
            try {
                request.setCharacterEncoding("UTF-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        if (!RequestUtil.isMultipart(request)) {
            return new BaseState(false, AppInfo.NOT_MULTIPART_CONTENT);
        }
        try
        {
            UploadFileAction uploadFileAction = new UploadFileAction();
            uploadFileAction.setEnv(getEnv());
            uploadFileAction.setRequest(request);
            uploadFileAction.setResponse(response);
            uploadFileAction.setConfig(config);
            uploadFileAction.setLanguage(language);
            uploadFileAction.initialize();
            uploadFileAction.setMultipartRequest(new MultipartRequest(request,uploadFileAction.getSaveDirectory()));
            uploadFileAction.setUploadFileDAO(uploadFileDAO);
            uploadFileAction.setChineseAnalyzer(chineseAnalyzer);
            uploadFileAction.setUseOriginalDate(true);
            uploadFileAction.execute();
            JSONObject json = (JSONObject)uploadFileAction.getResult();
            BaseState state =  new BaseState(true, json.getString("url"));
            state.setJson(json);
            if (json.getBoolean("success")||json.getBoolean("OK"))
            {
                return state;
            }
            return new BaseState(false, AppInfo.NOTFOUND_UPLOAD_DATA);
        } catch (Exception e) {
            e.printStackTrace();
            return new BaseState(false, AppInfo.PARSE_REQUEST_ERROR);
        }
    }

    private State base64Save(String content,String saveDirectory,String setupPath,long maxSize) throws Exception {

        byte[] data = com.jspx.security.utils.Base64.decode(content, com.jspx.security.utils.Base64.DEFAULT);

        if (!validSize(data, maxSize)) {
            return new BaseState(false, AppInfo.MAX_SIZE);
        }

        String suffix = FileType.getSuffix("JPG");
        FileRenamePolicy fileRenamePolicy = TXWebUtil.getFileRenamePolicy("JspxNetFileRenamePolicy");
        File file = new File(saveDirectory, System.currentTimeMillis() + suffix);
        try {
            file = FileUtil.moveToTypeDir(file, fileRenamePolicy, false);
        } catch (IOException e) {
            return new BaseState(false, AppInfo.IO_ERROR);
        }
        if (file==null)
            return new BaseState(false, AppInfo.IO_ERROR);

        boolean thumbnail = getBoolean("thumbnail");
        int maxImageWidth = config.getInt(Environment.maxImageWidth, 1280);

        State state = StorageManager.saveBinaryFile(data, file.getAbsolutePath());

        //------------------------------------------------------hash算法 判断重复
        String url = "/" + uploadFileDAO.getNamespace() + "/" +  FileUtil.getDecrease(file.getAbsolutePath(), setupPath);
        url = FileUtil.mendFile(url);

        if (state.isSuccess()) {
            state.putInfo("url", url);
            state.putInfo("type", suffix);
            state.putInfo("original", "");
        }

        Object uploadFile = uploadFileDAO.getClassType().newInstance();
        IUploadFile upFile = (IUploadFile) uploadFile;
        upFile.setTitle("none");
        upFile.setFileName(url);
        upFile.setFileSize(file.length());
        upFile.setFileType(suffix);
        upFile.setContent("none");

        IUserSession userSession = (IUserSession) getUserSession();
        if (userSession!=null)
        {
            upFile.setPutName(userSession.getName());
            upFile.setPutUid(userSession.getUid());
            upFile.setIp(getRemoteAddr());
        }
        upFile.setHash(FileUtil.getFastHash(file, UploadFileAction.hashType));

        Object alreadyUploadFile =  uploadFileDAO.getForHash(upFile.getHash());
        IUploadFile checkUploadFile = (IUploadFile)alreadyUploadFile;
        if (!StringUtil.isNULL(upFile.getHash()) && alreadyUploadFile != null && checkUploadFile.getHash().equalsIgnoreCase(upFile.getHash()))
        {
            if (checkUploadFile.getPutUid() != userSession.getUid()) {
                //不同用户发布的，转存一份
                checkUploadFile.setId(0);
                checkUploadFile.setPutName(userSession.getName());
                checkUploadFile.setPutUid(userSession.getUid());
                checkUploadFile.setIp(getRemoteAddr());
                checkUploadFile.setDownTimes(0);
                uploadFileDAO.save(alreadyUploadFile);
            }

            File oldFile = UploadFileAction.getUploadFile(config,checkUploadFile.getFileName());
            state = new BaseState(true, oldFile.getAbsolutePath());
            state.putInfo("size", checkUploadFile.getFileSize());
            state.putInfo("title", checkUploadFile.getTitle());
            state.putInfo("url", checkUploadFile.getFileName());
            state.putInfo("type", checkUploadFile.getFileType());
            state.putInfo("original", "");
            FileUtil.delete(file);
            return state;

        } else if (uploadFileDAO != null) {

            uploadFileDAO.save(upFile);

            //已经上传成功的,还没有的上传上去的就上传上去
            if (FileSuffixUtil.isImageSuffix(upFile.getFileType())) {
                //如果是图片就得到宽高
                BufferedImage image = ImageIO.read(file);
                int w = image.getWidth();
                int h = image.getHeight();
                if (image.getWidth() > maxImageWidth) {
                    h = (maxImageWidth / w) * h;
                    File tempFile = FileUtil.createFile(new File(file.getParent(), System.currentTimeMillis() + "_thumbnail.tmp"));
                    boolean repair = ImageUtil.thumbnail(new FileInputStream(file), new FileOutputStream(tempFile), suffix, maxImageWidth, h) && FileUtil.copy(tempFile, file, true);
                    state.putInfo("repair", repair + "");
                }
                upFile.setAttributes("width=" + w + "\r\nheight=" + h);
                String thumbnailImg = "s_" + upFile.getFileName();
                int width = config.getInt("thumbnailWidth", 400);
                int height = config.getInt("thumbnailHeight", 400);
                if (thumbnail && ImageUtil.thumbnail(new FileInputStream(file), new FileOutputStream(new File(file.getParent(), thumbnailImg)), suffix, width, height)) {
                    state.putInfo("fileName", thumbnailImg);
                    state.putInfo("name", FileUtil.getNamePart(thumbnailImg));
                    state.putInfo("thumbnail", 1);
                    state.putInfo("type", suffix);
                    state.putInfo("url", '/' + uploadFileDAO.getNamespace() + '/' + FileUtil.mendPath(FileUtil.getDecrease(setupPath, file.getParent())) + thumbnailImg);
                    return state;
                }
            }
        }
        return state;
    }

    private static boolean validSize(byte[] data, long length) {
        return data.length <= length;
    }

    public String execute() throws Exception {
        configManager = ConfigManager.getInstance(Dispatcher.getRealPath(),uploadFileDAO.getNamespace());
        String callbackName = getString("callback");
        String result;
        if (!StringUtil.isNULL(callbackName)) {
            if ( !validCallbackName( callbackName ) ) {
                result = new BaseState( false, AppInfo.ILLEGAL ).toJSONString();
            } else
                result =callbackName+"(" + this.invoke() + ");";
        } else {
            result = this.invoke();
        }

        TXWebUtil.print(result, TXWebUtil.jsonType, response);
        return NONE;
    }
}
